<template>
  <el-dialog :visible.sync="visible" :title="$t('oss.upload')" :close-on-click-modal="false" :close-on-press-escape="false" v-dialogDrag :modal="false" custom-class="upload-dialog"
    top="0" :show-close="false">
    <div class="upload-list">
      <div class="upload-list-head">
        <div class="upload-list-head-col" style="width: calc(100% - 120px - 160px)">文件名</div>
        <div class="upload-list-head-col" style="width: 120px">大小</div>
        <div class="upload-list-head-col" style="width: 160px">状态</div>
      </div>

      <div class="upload-list-row" v-for="(item,index) in fileList" :key="index">
        <div class="process" :style="'width:'+ item.process +'%;'"></div>
        <div class="upload-list-row-col text-overflow" style="width: calc(100% - 120px - 160px)" :title="item.name">{{ item.name }}</div>
        <div class="upload-list-row-col text-overflow" style="width: 120px" :title="bytesToSize(item.size)">{{ bytesToSize(item.size) }}</div>
        <div class="upload-list-row-col text-overflow" style="width: 160px">
          <span v-if="item.status==1">
            <span style="display: inline-block;width: 50px">{{ formatTow(item.process) }}%</span>
            <span v-if="item.speed">({{ item.speed }})</span>
          </span>
          <span v-if="item.status==2">取消</span>
          <span v-if="item.status==3">暂停</span>
          <span v-if="item.status==4">完成</span>
          <span v-if="item.status==5">失败</span>
          <span v-if="item.status==6">处理中...</span>
        </div>
      </div>
    </div>
  </el-dialog>
</template>

<script>
import numberUtils from '@/utils/NumberUtils.js' // 计算相关工具
import { getUUID } from '@/utils'
import { LimitPromise } from '@/utils/limit-promise.js' // 并发队列
// 请求上限
const MAX = 10
// 核心控制器
const limitP = new LimitPromise(MAX)
export default {
  data () {
    return {
      visible: false,
      fileList: [],
      /* 切片大小 MB */
      sectionSize: 1
    }
  },
  computed: {
    formatTow () {
      return (price) => {
        return numberUtils.divide(Math.floor(numberUtils.multiply(price, 100)), 100)
      }
    },
    bytesToSize () {
      return (bytes) => {
        let temp = bytes
        let units = ''
        if (bytes < 1024) {
          units = 'B'
        } else if (bytes < (1024 * 1024)) {
          temp = numberUtils.divide(bytes, 1024)
          units = 'KB'
        } else if (bytes < (1024 * 1024 * 1024)) {
          temp = numberUtils.divide(bytes, (1024 * 1024))
          units = 'MB'
        } else {
          temp = numberUtils.divide(bytes, (1024 * 1024 * 1024))
          units = 'GB'
        }
        return this.formatTow(temp) + units
      }
    }
  },
  methods: {
    // 追加文件
    pushFile (fileData) {
      this.fileList.push(fileData)
      this.uploadFile(fileData)
    },
    // 上传之前
    beforeUploadHandle (file) {
      if (file.type !== 'image/jpg' && file.type !== 'image/jpeg' && file.type !== 'image/png' && file.type !== 'image/gif') {
        this.$message.error(this.$t('upload.tip', { format: 'jpg、png、gif' }))
        return false
      }
      this.num++
    },
    // 上传文件-校验
    uploadFile (fileData) {
      if (!fileData || !fileData.file) { // 判断文件是否存在
        return
      }

      this.uploadBySplit(fileData, getUUID(), 0)
    },
    // 分片上传
    uploadBySplit (fileParam, identifier) {
      const file = fileParam.file
      const chunkSize = numberUtils.multiply(numberUtils.multiply(1024, 1024), this.sectionSize) // 分片大小1M
      const size = file.size // 总大小
      const totalChunks = Math.ceil(numberUtils.divide(size, chunkSize))
      const fileChunkedList = [] // 文件分片完成之后的数组

      // 计算每一篇的起始位置和结束位置
      for (let i = 0; i < totalChunks; i++) {
        const start = numberUtils.multiply(i, chunkSize)
        const end = Math.min(size, numberUtils.add(start, chunkSize))
        const fileData = file.slice(start, end)
        fileChunkedList.push(fileData)
      }
      fileChunkedList.forEach((fileData, i) => {
        // 文件分块上传
        const reader = new FileReader()
        reader.readAsBinaryString(fileData)
        reader.onload = (e) => {
          const formData = new FormData()
          formData.append('chunkNumber', i + 1) // 当前第几片，从0开始，文件下表从1计算。
          formData.append('chunkSize', chunkSize) // 当前分片大小
          formData.append('currentChunkSize', fileData.size) // 当前块大小
          formData.append('totalSize', size) // 总的大小
          formData.append('identifier', identifier) // 唯一标识
          formData.append('filename', file.name) // 文件名
          formData.append('type', file.type) // 文件类型
          // formData.append('relativePath', "/") //相对路径，暂时没用
          formData.append('totalChunks', totalChunks) // 总片数
          formData.append('file', fileData) // 文件
          // 上传
          limitP.call(this.uploadHandle, formData, fileParam, chunkSize, size, i, totalChunks)
          /* i++
          this.uploadBySplit(fileParam, identifier, i) */
        }
      })
    },
    uploadHandle (formData, fileParam, chunkSize, size, i, totalChunks) {
      if (fileParam.status === 1) { // 只有当前文件处理中才进行上传
      // 必须用这个类型的头，并且要包括boundary
        return this.$http.post('/sys/oss/upload', formData, {
          headers: {
            'Content-Type': 'multipart/form-data； charset=utf-8; boundary="another cool boundary";'
          },
          onUploadProgress: progressEvent => {
          // this.progressHandle(fileParam, progressEvent.loaded, size)
          }
        }).then(({ data: res }) => {
          if (res.code === 0) {
            // 记录已经上传成功的个数
            fileParam.successCounts += 1
            // 进度条及速度计算
            this.progressHandle(fileParam, chunkSize, size)
            // 如果已经上传成功的个数等于总数，则整个文件成功
            if (fileParam.successCounts === totalChunks) {
              return this.uploadSuccess(formData, fileParam)
            }
          } else if (fileParam.status === 1) {
            // fileParam.status===1是避免多次提示
            fileParam.status = 5
            this.$message({ message: '上传失败', type: 'fail' })
          }
        }).catch((err) => {
          fileParam.status = 5
          console.log(err)
        })
      } else {
        return new Promise((resolve, reject) => {
          resolve()
        })
      }
    },
    uploadSuccess (formData, fileParam) {
      const dataForm = {}
      formData.forEach((item, key) => {
        if (key !== 'file') {
          dataForm[key] = item
        }
      })
      fileParam.status = 6
      return this.$http.post('/sys/oss/merge', dataForm).then(({ data: res }) => {
        if (res.code !== 0) {
          return this.$message.error(res.msg)
        }
        fileParam.status = 4
        this.$emit('refreshDataList', true)
      }).catch(() => {
        fileParam.status = 5
      })
    },
    progressHandle (fileParam, curLoaded, total) {
      // 上一次计算时间
      let lastTime = fileParam.lastTime || 0
      // 上一次计算的文件大小
      let lastSize = fileParam.lastSize || 0
      // 区间内已传输字节大小
      const loaded = numberUtils.add((fileParam.intervalSize || 0), curLoaded)
      fileParam.intervalSize = loaded
      fileParam.curSize = numberUtils.add((fileParam.curSize || 0), curLoaded)
      // 上传进度
      const process = numberUtils.multiply(numberUtils.divide(fileParam.curSize, total), 100)
      setTimeout(() => {
        fileParam.process = process > 100 ? 100 : process
      }, 0)
      /* 计算间隔 */
      const nowTime = new Date().getTime()
      const intervalTime = numberUtils.divide(numberUtils.subtract(nowTime, lastTime), 1000)// 时间单位为毫秒，需转化为秒
      if (!fileParam.isNew && intervalTime < 1) {
        return
      }
      // 是否初次载入
      fileParam.isNew = false
      fileParam.intervalSize = 0
      const intervalSize = loaded - lastSize
      // 记录方便下次计算
      fileParam.lastTime = nowTime
      fileParam.lastSize = lastSize
      /* 重新赋值以便于下次计算 */
      lastTime = nowTime
      lastSize = loaded

      /* 计算速度 */
      const speed = numberUtils.divide(intervalSize, intervalTime)
      // 计算速度
      fileParam.speed = this.bytesToSize(speed) + '/s'
      /* 计算剩余时间 */
      /* let bSpeed = speed// 保存以b/s为单位的速度值，方便计算剩余时间
      let leftTime = ((total - loaded) / bSpeed)
      console.log('当前速度：' + fileParam.speed + '   预计剩余时间：' + leftTime.toFixed(1) + '秒') */
    }
  }
}
</script>
<style scoped>
.el-dialog__wrapper{
  position: unset !important;
}
</style>
<style>
.upload-dialog{
  border-top-left-radius: 7px;
  border-top-right-radius: 7px;
  /*border: 1px solid #e2e2e2;*/
  box-shadow: 0 0 10px #ccc;
  margin: 0;
  height: 354px;
  position:absolute;
  bottom: 0;
  right: 0;
}
.upload-dialog .el-dialog__header{
  padding:10px 20px 10px;
}
.upload-dialog .el-dialog__header .el-dialog__title{
  font-size: 15px;
  font-weight: 200;
}

.upload-dialog .el-dialog__body{
  padding: 0;
}

.upload-dialog .text-overflow{
  overflow: hidden;
  text-overflow:ellipsis;
  white-space: nowrap;
}
.upload-dialog .el-table tbody tr {
  background-color: transparent;
}

/*进度条*/
.upload-list-row .process{
  position: absolute;
  top: 2px;
  left: 0;
  z-index: 0;
  height: calc(100% - 2px);
  background: #e2eeff;
}

/*list*/
.upload-list{
  overflow-y: auto;
  height: 300px;
}
.upload-list-head,.upload-list-row{
  position: relative;
}
.upload-list-head{
  border-top: 1px solid #f2f6fd;
  /*background: #F5F7FA;*/
  height: 35px;
  line-height: 35px;
}
.upload-list-row{
  height: 50px;
  line-height: 50px;
}
.upload-list-head .upload-list-head-col,.upload-list-row .upload-list-row-col{
  display: inline-block;
  padding-left: 10px;
  padding-right: 10px;
  border-bottom: 1px solid #f2f6fd;
  position: relative;
  z-index: 1;
}
.upload-list-head .upload-list-head-col{
  font-size: 12px;
  font-weight: 200;
  overflow: hidden;
  color: #424e67;
}
</style>
